<?php
/**
 * Class description
 * 
 * @package System
 * @subpackage System_Request
 * @author Peter Matsis
 */
class System_Request_Controller {
    /**
     * constant to hold the default request for
     *
     * @var string
     */
    const DEFAULT_COMMAND_OBJECT = 'Command_Home_Index';
    
    /**
     * constant to hold the default response type
     *
     * @var string
     */
    const DEFAULT_RESPONSE_OBJECT = 'System_Request_Response_Type_Html';
    
    /**
     * constant to hold the default response state type
     *
     * @var string
     */
    const DEFAULT_RESPONSE_STATUS_TYPE = 'default';
     
    /**
     * tells the request to check against the session cookie or not
     * 
     * @var boolean
     * @access private
     */
    private $_cookie_check = true;
    
    /**
     * tells the request to check against the form token or not
     * 
     * @var boolean
     * @access private
     */
    private $_token_check = true;
    
    /**
     * tells the request to check for an api key or not
     * 
     * @var boolean
     * @access private
     */
    private $_api_key_check = true;
    
    /**
     * tells the response object to output headers or not
     * 
     * @var boolean
     * @access private
     */
    private $_output_headers = true;
    
    /**
     * holds the current request object
     * 
     * @var boolean|Object 
     * @access private
     */
    private $_request_object = false;
    
    /**
     * holds the current response object
     * 
     * @var boolean|Object 
     * @access private
     */
    private $_response_object = false;
    
    /**
     * holds the current response object
     * 
     * @var boolean|Object 
     * @access private
     */
    private $_response_state_object = false;
    
    /**
     * Stores the data in a property of the class
     * 
     * @var array
     * @access private
     */
    private $_data = array();
    
    /**
     * Stores an instance of the error class
     * 
     * @var Object - Instance of System_Error_Base
     * @access private
     */
    private $_error_object;
    
    /**
     * tells the response_object to return headers
     *
     * @var boolean
     * @access private
     */
    private $_set_headers = true;
    
    /**
     * holds an instance of the registry
     *
     * @var boolean
     * @access private
     */
    private $_registry;
    
    /**
     * holds the data for the request
     * 
     * @var array
     * @access private
     */
    private $_command_data = array();
    
    /**
     * Class Constructor
     *
     * @access public
     * @return System_Request_Controller
     */     
    public function __construct(array $data = array()){
        $this->setData($data);
        $this->_registry = System_Registry_Root::getInstance();
        $this->_error_object = System_Error_Base::getInstance();
        
        return $this;
    }
    
    /**
     * Allows a command to be called internally
     *
     * @param string $command - the name of the command to be executed
     * @param string $response_type - the type of response to be retuned upon execution. Defaults to array
     * @access public
     * @static
     * @throws System_Request_Exception - if the command is left blank
     * @return System_Request_Controller
     */
    public static function callCommand($command, $response_type = 'array'){
        if(trim($command) == '') throw new System_Request_Exception('You must provide a command');
        
        $data = array(
            'command' => $command,
            'response_type' => $response_type
        );
        
        $data = array_merge(System_Request_Router::getDefaultSetup(), $data);
        
        return new self($data, System_Registry_Root::getInstance());
    }
    
    /**
     * this method sets up the request
     * 
     * @access public
     * @return System_Request_Controller
     */
    public function setUp(){
        $command                    = self::DEFAULT_COMMAND_OBJECT;
        $response                   = self::DEFAULT_RESPONSE_OBJECT;
        $use_default_command        = false;
        $use_default_response       = false;
        
        /**
         * if the process is allowed, set the request and response objects
         * else, set the class to the default_request_object
         */
        if($this->verifyRequest()){
            $command = $this->_data['command'];
            
            if(!class_exists($command)) $use_default_command = true;
        }else{
            $use_default_command = true;
        }
        
        /**
         * sets the response object property
         * if use_default is true, it will use the default response_object
         */
        if(isset($this->_data['response_type'])){
            $response = 'System_Request_Response_Type_' . ucfirst($this->_data['response_type']);

            if(!class_exists($response)) $use_default_response = true;
        }else{
            $use_default_response = true;
        }
        
        /**
         * set the response_state type
         */
        $response_state_type = isset($this->_data['response_status_type']) ? $this->_data['response_status_type'] : self::DEFAULT_RESPONSE_STATUS_TYPE;
        
        /**
         * get the objects
         */
        $command_object         = $use_default_command ? new $command($this->_data) : new $command($this->_data);   
        $response_object        = $use_default_response ? new $response : new $response;
        $response_state_object  = new System_Request_Response_Status($response_state_type);

        /**
         * check the command's access against the current user
         * if it fails, reassign the command_object to the Command_Error_Access
         * 
         * TODO: System_Security_Access is a mess fix it
         */
        if(!System_Security_Access::isRootAnd($this->_data['access'])){
            $command_object = new Command_Error_401($this->_data);
        }

        /**
         * sets the registry and data for the command object
         */
        $command_object->setRegistry($this->_registry)->setData($this->getCommandData());

        /**
         * set the request, response, and response_state objects
         * then register the response state object and error object with the request object
         */
        $this->setCommandObject($command_object)->setResponseObject($response_object)->setResponseStateObject($response_state_object)->registerRequestObjects();    
        
        return $this;
    }
    
    /**
     * this method will merge the command data with the allowed data -- get or post based on the routing rules
     * 
     * @access private
     * @return array - the data that will be set to the command object
     */
    private function getCommandData(){        
        if($this->_data['allow'] != 'none'){
            if(preg_match('/post/i', $this->_data['allow']) && count($_POST)) $this->_command_data = array_merge($this->_command_data, $_POST);
            if(preg_match('/get/i', $this->_data['allow']) && count($_GET)) $this->_command_data = array_merge($this->_command_data, $_GET);
        }
        
        return $this->_command_data;
    }
    
    /**
     * this method is used to define the command data that is passed to the command object
     *
     * @param array $data -- the array of data that you want passed.
     * @access public
     * @return System_Request_Controller
     */
    public function setCommandData($data = array()){
        $this->_command_data = $data;
        
        return $this;
    }
    
    /**
     * sets the request_object property
     *
     * @param Object $object - Instance of System_Request_Interface
     * @access public
     * @return Object - System_Request_Controller
     */
    public function setCommandObject($object){
        $this->_command_object = $object;

        return $this;
    }
    
    /**
     * sets the response_object property
     *
     * @param Object $object 
     * @access public
     * @return Object - System_Request_Controller
     */
    public function setResponseObject($object){
        $this->_response_object = $object;

        return $this;
    }
    
    /**
     * sets the response_state_object property
     *
     * @param Object $object - Instance of System_Request_Interface
     * @access public
     * @return Object - System_Request_Controller
     */
    public function setResponseStateObject($object){
        $this->_response_state_object = $object;

        return $this;
    }
    
    /**
     * registers the response_state_object and error_object with the request_object
     *
     * @access public
     * @return Object - System_Request_Controller
     */
    public function registerRequestObjects(){
        $this->_command_object->registerResposeStateObject($this->_response_state_object)->registerErrorObject($this->_error_object);
        
        return $this;
    }
    
    /**
     * sets the internal_request property to true
     *
     * @access public
     * @return object, instance of this class
     */
    public function internal(){
        $this->_internal_request    = true;
        $this->_output_headers      = false;
        
        $this->noCookieCheck()->noTokenCheck();
        
        return $this;
    }
    
    /**
     * sets the cookie_check property to false
     *
     * @access public
     * @return object, instance of this class
     */
    public function noTokenCheck(){
        $this->_token_check = false;
        
        return $this;
    }
    
    /**
     * sets the cookie_check property to false
     *
     * @access public
     * @return object, instance of this class
     */
    public function noCookieCheck(){
        $this->_cookie_check = false;
        
        return $this;
    }
    
    /**
     * verifies the current request's session cookie value and form token
     *
     * @access private
     * @return boolean
     */
    private function verifyRequest(){
        $continue = true;
        
        /**
         * check the form token
         */
        if($this->_token_check && !$this->verifyToken()){
            //$continue = false;
        }
    
        /**
         * check the cookie value
         */
        if($this->_cookie_check && !$this->verifyCookie()){
            $continue = false;
        }
        
        /**
         * check the api key
         */
        if($this->_api_key_check && !$this->apiKeyCheck()){
            $continue = false;
        }
        
        return true;
    }
    
    /**
     * method that checks the session cookie against the data being sent to the request
     *
     * @access private
     * @return boolean
     */
    private function verifyToken(){
        $return = true;
        
        if(!isset($this->_data['form_id']) || !System_Request_Token::check($this->_data['form_id'], $this->_data['token'])){
            $return = false;
        }
        
        return $return;
    }
    
    /**
     * method that checks the session cookie against the data being sent to the request
     *
     * @access private
     * @return boolean
     */
    private function verifyCookie(){
        $return = true;
        
        if(!isset($this->_data['PHPSESSID']) || $this->_data['PHPSESSID'] != $_COOKIE['PHPSESSID']){
            $return = false;
        }
        
        return $return;
    }
    
    /**
     * checks for a valid API_KEY
     *
     * @access private
     * @return boolean
     */
    private function apiKeyCheck(){
        return true;
    }

    /**
     * Sets the data property with the data needed
     * 
     * @access public
     * @return mixed
     */
    public function setData($data){
        $this->_data = $data;
        
        return $this;
    }
    
    /**
     * method that processes the request
     *
     * @access public
     * @return Mixed - the results of the request
     */
    public function process(){
        $this->_command_object->doExecute();

        $message = $this->_command_object->getMessage();
        
        if($this->_output_headers) $this->_response_object->headers();

        return $this->_response_object->setData($message)->processResponse();
    }
}